struts2总结(一)--简介和Action

1. 简介

Apache Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。Struts2是Struts1和WebWork的技术基础上进行了合并的全新的Struts2框架。其全新的Struts2的体系结构与Struts1的体系结构差别巨大。Struts2以WebWork为核心,采用拦截器的机制来处理用户的请求,这样的设也是的业务逻辑控制器能够ServletAPI完全脱离开。

1.1 Struts2的优点

  1. 在软件设计上,Struts2没有像struts1那样和Servlet API和struts API有着紧密的耦合,Struts2的应用可以不依赖于Servlet API和Struts2 API,属于无侵入式设计。
  2. Struts2提供了拦截器,利用拦截器可以进行AOP(面向切面编程),实现注入权限拦截等功能。
  3. Struts2提供了类型转换器,可以把特殊的请求参数转换为需要的类型。
  4. Struts2提供支持多种表现层技术,如JSP,freeMarker, Velocity等
  5. Struts2的输入校验可以对指定方法进行校验
  6. 提供了全局范围,包范围和Action范围的国际化资源文件管理实现

1.2 Web项目中的三层框架

1.2.1 表示层

Struts2框架就是工作在这里

1.2.2 业务逻辑层

Service层,处理业务逻辑,比如判断用户名是否存在,用户名和密码是否匹配,权限检查等。为一些执行某一个操作条件的判断。

1.2.3 数据访问层

dao层,专门处理和数据库进行交换的业务。jdbc/hibernate在这里使用。MVC架构是项目中三层架构的一部分,三层架构的表示层可以使用MVC架构的struts2框架来实现。

2. web项目中引入struts2框架

2.1 开发环境

  • IDE:Intellij IDEA 2017.2
  • Server:Tomcat 9.0
  • JDK:JDK 8

2.2 新建项目

File-New-Project
创建JavaEE项目,勾选WebApplication和Struts2选项。

2.3 目录结构

  • Project Name
    • .idea
    • lib
    • out
    • src
    • web
    • xx.iml
      需要注意将lib移动到web目录下面的Web-INF文件夹下,并在moudule中添加路径,不然部署过程中会报ClassNotFoundException。因为部署过程中只会读取web下的lib, 外部的lib是不会读取的。

2.4 struts2配置文件

src目录下面会生成struts.xml文件,默认内容如下:

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
</struts>

2.5 web.xml文件中配置struts2框架的过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

这个过滤器的作用是拦截struts2框架中的action

3 Action

3.1 Action定义

  • struts2框架中有一种Java类叫做Action。struts2框架楼底层封装了Servlet的相关内容而实现出来的。替代之前Servlet的类在Struts2中成为Action。比Servlet功能更加强大,操作更加简单。原因在于拦截器的设计,拦截器可以拦截访问Action的请求,拦截请求之后,可以给Action添加很多丰富的功能,而Action专注于业务逻辑的处理就可以。

3.2 实现一个Action

3.2.1 类中只要有一个固定的方法:

1
2
3
4
5
public class IndexAction1 {
public String execute() {
return "success";
}
}

不需要实现或继承任何接口或者父类。execute方法一定要返回String类型的对象,每个字符串都可以对应一个跳转的页面。(字符串是可以自己随便定义的,字符串对应的哪一个跳转到页面也是自己定义的,在struts.xml文件中定义这些内容)

3.2.2 实现一个接口:com.opensyymphony.xwork2.Action

1
2
3
4
5
6
7
8
9
10
package com.struts2.front.action;
import com.opensymphony.xwork2.Action;
public class IndexAction2 implements Action{
@Override
public String execute() throws Exception {
return "success";
}
}

该接口有一个抽象方法execute()和五个静态属性:ERROR,SUCCESS, INPUT NONE, LOGIN

3.2.3 继承一个指定的父类ActionSupport

1
2
3
4
5
6
7
8
9
10
package com.struts2.front.action;
import com.opensymphony.xwork2.ActionSupport;
public class IndexAction3 extends ActionSupport{
@Override
public String execute() {
return "success";
}
}

这种方法是最常用的,继承ActionSupport类,重写execute()方法。实际上ActionSupport实现了Action接口。

3.3 在struts.xml中进行配置

配置之后,才可以通知struts2框架我们写的这个类是一个Action,将来struts2框架中要给这个类创建对象,调用方法以及这个Action加入更多丰富的功能,注册的实例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<package name="front" extends="struts-default" namespace="/">
<action name="index" class="com.struts2.front.action.IndexAction1">
<result name="success">/ActionIntroduction.jsp</result>
</action>
</package>
</struts>

  • <package>: 一个struts.xml文件中可以配置多个<package>标签,一个<package>标签里面可以配置多个<action>标签,一个<action>标签里面可以配多个<result>标签
  • name=”front” 表示给当前package起一个名字,唯一标示当前这个package,方便package之间的继承
  • extends=”struct-default” 表示当前这个package继承了另外一个名字叫做struct-default的package。这个package定义在core包中的struts-default.xml中定义。所有的package都会直接或者间接的继承struts-default这个包,类似java中的Object类。
  • namespace=”/“ 表示当前package的命名空间为/,以后这个package里面所有的action被访问时候,路径里面都要加上这个命名空间
  • <action>标签属性及其子标签:
    • name=”index” 表示当前配置这个action的名字为index,这个名字是自己定义的,可以和action类的名字相同,同时将来浏览器中的地址栏里面就要出现这个名字来访问当前这个action类
    • class=”com.struts2.front.action.IndexAction1” 表示当前配置的action对应的是IndexAction1这个类,
    • <result> 标签表示action访问完成之后,根据action返回的字符串的值,跳转不同的页面,默认结果为’success’, 可以省略,一般都是Action接口中的那5个返回值,也可以自己定义。

3.4 动态方法调用DMI

struts2默认调用的是action中的execute方法,实际上我们也可以让其调用其他的方法,首先定义一个类,让其继承ActionSupport类,配置的时候配置其方法,或者不写具体的方法,用DMI的方式进行调用:

1
2
3
4
5
6
7
8
9
package com.struts2.user.action;
import com.opensymphony.xwork2.ActionSupport;
public class UserAction extends ActionSupport{
public String add() {
return SUCCESS;
}
}

对其进行配置struts2.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<struts>
<!--<constant name="struts.enable.DynamicMethodInvocation" value="true"/>-->
<!--允许默认动态方法调用,Struts2默认不支持此功能-->
<constant name="struts.enable.DynamicMethodInvocation" value="true"/>
<constant name="struts.devMode" value="true" />
<package name="user" extends="struts-default" namespace="/user">
<action name="userAdd" class="com.struts2.user.action.UserAction" method="add">
<result>/user_add_success.jsp</result>
</action>
<action name="user" class="com.struts2.user.action.UserAction">
<result>/user_add_success.jsp</result>
</action>
</package>
</struts>

其中需要注意的是:

  • struts.enable.DynamicMethodInvocation 默认为false,将其设置为true
  • 配置action的时候,
  • 浏览器中的访问方法为:../user/user!add 动态方法调用, 或者../user/userAdd 都可以成功访问

3.5 使用通配符来配置action

从之前的学习中可以发现,多个action名字或者类的名字是有一定的规律的,比如我们要写两个action, StudentAction, TeacherAction, 每个action有两个方法,add和execute方法,配置的时候可以使用通配符来简化配置:

1
2
3
4
5
6
7
8
9
10
11
12
<struts>
<constant name="struts.devMode" value="true" />
<package name="actions" extends="struts-default" namespace="/actions">
<action name="Student*" class="com.struts2.action.StudentAction" method="{1}">
<result>/Student{1}_success.jsp</result>
</action>
<action name="*_*" class="com.struts2.action.{1}Action" method="{2}" >
<result>/{1}_{2}_success.jsp</result>
</action>
</package>
</struts>

其中{1}代表的是前面name属性中的第一个*号的值。这样可以简化配置,如果约定的好的话,整个配置文件可以只配置一个action。但是需要记住一点: 约定优于配置!!

3.6 action 的特点及其访问

3.6.1 特点:

  • Servlet 是线程不安全的,因为Servlet是单例
  • struts2 框架中的action是线程安全的,因为每次访问都会创建一个新的Action对象,所有的action里面可以随意定义成员变变量(只有成员变量才有线程安全的问题)

    3.6.2 访问:

    默认情况下,namespace/actionName.action或者namespace/actionName即可访问到action。访问成功会跳转到相应的jsp页面。由于这样会影响对相关Servlet的访问,所有最好默认action访问使用后缀名.action
  • 访问action的时候,action中方法的调用
    1. 默认情况下,访问action的 时候会调用action的execute()方法,这个方法执行完返回一个字符串,根据字符串进行跳转
    2. 可以在action标签里面添加一个method属性来指明将来访问这个action的时候会调用的方法
    3. 地址栏中动态指定调用的方法

3.7 Action接收参数

3.7.1 同名参数

例如通过页面将name=a&age=8这个参数传送给action,这时候需要:

  • action里面定义两个成员变量;name,age
  • action里面需要提供get/set方法
  • 当页面将参数传送过来的时候,struts2框架会自动帮我们把这两个参数的值放在action中的属性里面,并自动进行了类型转换。从而完成了参数的初始化,实际上这个是有defaultStack这个拦截器栈里面的拦截器来完成的

index.jsp:

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
使用action属性接受参数<a href="user/user!add?name=a&age=8">添加用户</a>
</body>
</html>

struts.xml:

1
2
3
4
5
6
7
8
9
<struts>
<constant name="struts.devMode" value="true"/>
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
<package name="user" extends="struts-default" namespace="/user">
<action name="user" class="com.struts2.user.action.UserAction">
<result>/user_add_success.jsp</result>
</action>
</package>
</struts>

UserAction:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.struts2.user.action;
import com.opensymphony.xwork2.ActionSupport;
public class UserAction extends ActionSupport{
private String name;
private int age;
public String add() {
System.out.println("name = " + name);
System.out.println("age = " + age);
return SUCCESS;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

3.7.2 DomainModel 域模型

在接收到的页面传值的时候,可以让struts2框架直接帮我们把这些接收到的值封装到一个JavaBean对象里面:

  1. Action中定义一个User类型的变量user,User类中有name,age两个成员变量及其get/set方法
  2. UserAction中提供这个User的get/set方法
  3. 页面向Action传递参数的名称要求user.name=1&user.age=23

接收到这个参数之后,struts2框架会帮我们去创建一个User队形,并且将所传的参数封装到对象的三个属性中去,最后将这个封装好的对象放到Action中的user属性中去。

3.7.3 模型驱动

实现ModelDriven接口,没怎么接触,不常用!创建一个User类有name,age两个成员变量,以及一个UserAction类,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class UserAction extends ActionSupport implements ModelDriven<User>{
private User user = new User();
public String add() {
System.out.println("name=" + user.getName());
System.out.println("age=" + user.getAge());
return SUCCESS;
}
@Override
public User getModel() {
return user;
}
}

3.8 简单数据验证

Action可以进行简单数据验证,这种验证一般不使用UI标签就可以完成,添加addFieldError到页面:

1
2
3
4
5
6
7
8
public String add() {
if(name == null || !name.equals("admin")) {
this.addFieldError("name", "name is error");
this.addFieldError("name", "name is too long");
return ERROR;
}
return SUCCESS;
}

3.9 访问Web元素

访问Web元素有四种方法,Map类型的和原始类型的各两种:

  1. Map类型 依赖于容器:Action初始化的时候生成三个Action容器,获取网页元素

    1
    2
    3
    4
    5
    public LoginAction1() {
    request = (Map)ActionContext.getContext().get("request");
    session = ActionContext.getContext().getSession();
    application = ActionContext.getContext().getApplication();
    }
  2. Map类型 IOC,Action实现了RequestAware,SessionAware, ApplicationAware三个接口,有三个Map类型的成员变量,并重写了成员变量的set方法,struts会自动调用set方法将元素注入到Action中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public class LoginAction2 extends ActionSupport implements RequestAware, SessionAware, ApplicationAware{
    private Map<String, Object> request;
    private Map<String, Object> session;
    private Map<String, Object> application;
    public String execute() {
    request.put("r1", "r1");
    session.put("s1", "s1");
    application.put("a1", "a1");
    return SUCCESS;
    }
    @Override
    public void setApplication(Map<String, Object> application) {
    this.application = application;
    }
    @Override
    public void setRequest(Map<String, Object> request) {
    this.request = request;
    }
    @Override
    public void setSession(Map<String, Object> session) {
    this.session = session;
    }
    }
  3. 原始类型,IOC,实现了ServletRequestAware接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class LoginAction3 extends ActionSupport implements ServletRequestAware{
    private HttpServletRequest request;
    private HttpSession session;
    private ServletContext application;
    public String execute() {
    request.setAttribute("r1", "r1");
    session.setAttribute("s1", "s1");
    application.setAttribute("a1", "a1");
    return SUCCESS;
    }
    @Override
    public void setServletRequest(HttpServletRequest httpServletRequest) {
    this.request = request;
    this.session = request.getSession();
    this.application = session.getServletContext();
    }
    }
  4. 原始类型,依赖容器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class LoginAction4 extends ActionSupport{
    private HttpServletRequest request;
    private HttpSession session;
    private ServletContext application;
    public LoginAction4() {
    request = ServletActionContext.getRequest();
    session = request.getSession();
    application = session.getServletContext();
    }
    public String execute() {
    request.setAttribute("r1", "r1");
    session.setAttribute("s1", "s1");
    application.setAttribute("a1", "a1");
    return SUCCESS;
    }
    }
  • 原类型和map类型的关系
    1. 使用Map类型的对象,可以降低对ServletAPI的依赖
    2. 我们使用元类型大多时候也是存值和取值,原来性里面本身也封装了Map对象,使用Map类型对象也可以完成存值和取值
    3. map类型的request对象里面的k-v值是复制原类型request对象的键值对的,元类型的request对象中还包含其他的属性和方法。
    4. 原类型和Map类型的Request对象是相同的 ,可以互相读取值,session和application也一样。

3.10 包含文件配置

struts2支持配置文件之间的继承,通过include标签完成,相当于将include内容复制到include的位置:

  • struts.xml:

    1
    2
    3
    4
    <struts>
    <constant name="struts.devMode" value="true" />
    <include file="login.xml" />
    </struts>
  • login.xml:

    1
    2
    3
    4
    5
    6
    7
    <struts>
    <package name="login" extends="struts-default" namespace="/login">
    <action name="login*" class="com.bjsxt.struts2.user.action.LoginAction{1}">
    <result>/user_login_success.jsp</result>
    </action>
    </package>
    </struts>

3.11 默认Action处理

当struts中没有找到请求的Action的时候会报错,我们可以设置一个默认的Action,当找不到请求的action的时候访问这个特定的Action,使得用户体验更加友好。在package中配置一条语句就可以完成:

  • <default-action-ref name="index"></default-action-ref>
Donate comment here